package com.ruixin.config;

import com.ruixin.annotation.Bean;
import com.ruixin.annotation.Parser;
import com.ruixin.base.BaseTryCatch;
import com.ruixin.base.exception.BaseException;
import com.ruixin.base.util.BeanUtil;
import com.sun.istack.internal.NotNull;

import java.io.File;
import java.net.URL;
import java.util.*;

/**
 * Created by Ruixin on 2020/5/16.
 * Description:bean加载类
 */
public class ClassLoader {

    //存放class
    private Set<Class<?>> classes = new HashSet<>();

    //存放bean class
    private Set<Class<?>> beanClasses = new HashSet<>();

    //存放parserClass
    private Set<Class<?>> parserClasses = new HashSet<>();

    //存放ApplicationListener
    private Set<Class<ApplicationListener>> applicationListenerClasses = new HashSet<>();

    //存放包,防止扫描同一个包
    public Set<String> packages = new HashSet<>();

    //文件后缀
    private final String subffix = ".class";

    //获取加载bean class的集合

    public Set<Class<?>> getBeanClasses() {
        return beanClasses;
    }
    //parser集合类

    public Set<Class<?>> getParserClasses(){
        return parserClasses;
    }
    //所有的class

    public Set<Class<?>> getClasses() {
        return classes;
    }
    /**
     * 加载类
     * @param packages
     */
    public void load(@NotNull String packages) throws BaseException {
        String[] packs = packages.split(",");
        load(Arrays.asList(packs),getFilter());
    }

    /**
     * 加载类
     * @param packages
     * @throws BaseException
     */
    public void load(List<String> packages,ClassFilter classFilter) throws BaseException {
        BaseTryCatch.process(()->{
            Enumeration<URL> resources = ClassLoader.class.getClassLoader().getResources("");
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                if("file".equals(url.getProtocol())){
                    for (String pkg : packages) {
                        loadClass(url.getPath(),pkg, classFilter);
                    }
                }
            }
        });
    }

    private String getFilePath(String basePath,String pkg) throws BaseException {
        pkg = pkg.replaceAll("\\.","/");
        return basePath+File.separator+pkg;
    }

    /**
     * 加载类
     * @param pkg 包名
     * @throws BaseException
     */
    public void loadClass(String basePath,String pkg,ClassFilter classFilter) throws BaseException {
        try{
            String filePath = getFilePath(basePath,pkg);
            File dir = new File(filePath);
            if(dir==null|| !dir.exists()){
                return;
            }
            File[] files = dir.listFiles();
            for(File file:files){
                if(file.isDirectory()){
                    loadClass(basePath,pkg+"."+file.getName(),classFilter);
                }else{
                    String name = file.getName();
                    if(name.endsWith(subffix)){
                        String className = name.substring(0,name.lastIndexOf("."));
                        Class<?> aClass = BeanUtil.newClass(pkg + "." + className);
                        if(classFilter!=null){
                            boolean flag = classFilter.filter(className, aClass);
                            if(flag){
                                classes.add(aClass);
                            }
                        }else {
                            classes.add(aClass);
                        }
                    }
                }
            }
        }catch (Exception e){
            throw new BaseException("加载class出错",e);
        }
    }

    /**
     * 获取Filter
     * @return
     */
    private ClassFilter getFilter(){
        return (name, clz) -> {
            if(clz.isAnnotation()){
                return false;
            }
            //ApplicationListener
            if(!clz.isInterface() && ApplicationListener.class.isAssignableFrom(clz)){
                applicationListenerClasses.add((Class<ApplicationListener>)clz);
            }
            //parser
            if(BeanUtil.getAnnotation(clz, Parser.class)!=null){
                parserClasses.add(clz);
            }
            //Bean
            if(BeanUtil.getAnnotation(clz, Bean.class)!=null){
                beanClasses.add(clz);
            }
            return true;
        };
    }

    public Set<Class<ApplicationListener>> getApplicationListenerClasses() {
        return applicationListenerClasses;
    }

    public void setApplicationListenerClasses(Set<Class<ApplicationListener>> applicationListenerClasses) {
        applicationListenerClasses = applicationListenerClasses;
    }

    public interface ClassFilter{

        boolean filter(String name,Class<?> clz) throws BaseException;

    }

}
